package com.eng.carro.services;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;

@SuppressWarnings("unchecked")
public class BaseServiceProvider<T, ID extends Serializable> implements
	BaseService<T, ID> {

  @PersistenceContext(unitName = "projeto-java")
  EntityManager em;

  private Class<T> persistentClass;

  public BaseServiceProvider() {
  }

  /**
   * Retorna uma Session do hibernate retirada do EntityManager do JPA
   */
  public Session getSession() {
	return (Session) em.getDelegate();
  }

  public Class<T> getPersistentClass() {
	if (persistentClass == null) {
	  persistentClass = (Class<T>) ((ParameterizedType) this.getClass()
		  .getGenericSuperclass()).getActualTypeArguments()[0];
	}
	return persistentClass;
  }

  public T findById(ID id) {
	return (T) em.find(getPersistentClass(), id);
  }

  public List<T> findAll() {
	return findByCriteria(null);
  }
  	
  public List<T> findByExample(T exampleInstance, String[] excludeProperty) {
	Criteria crit = getSession().createCriteria(getPersistentClass());
	Example example = Example.create(exampleInstance);
	for (String exclude : excludeProperty) {
	  example.excludeProperty(exclude);
	}
	crit.add(example);
	return crit.list();
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  public T makePersistent(T entity) {
	try {
	  em.persist(entity);
	} catch (Exception e) {
	  e.printStackTrace();
	}
	
	return entity;
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  public T makeUpdate(T entity) {
	try {
	  em.merge(entity);
	} catch (Exception e) {
	  e.printStackTrace();
	}
	
	return entity;
  }
  
  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  public void delete(T entity) {
	try {
	  em.remove(entity);
	} catch (Exception e) {
	  e.printStackTrace();
	}
  }

  public void flush() {
	em.flush();
  }

  public void clear() {
	em.clear();
  }

  /**
   * Use this inside subclasses as a convenience method.
   */
  protected List<T> findByCriteria(Criterion... criterion) {
	if ( criterion == null ) {
	  TypedQuery<T> query = em.createQuery("select v from " + getPersistentClass().getSimpleName() + " v ", persistentClass);
	  return query.getResultList();
	}
	return null;
  }

  @Override
  public List<T> findByExample(T exampleInstance) {
	// TODO Auto-generated method stub
	return null;
  }

}
